package com.hery.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import com.github.wnameless.json.flattener.JsonFlattener;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * java Bean / JSON String 型转 Map
 */
@Slf4j
public class BeanToMap {

    private HashMap<String, Object> toMap(HashMap<String, Object> map, String clsName) throws IllegalAccessException {

        Field[] declaredFields = this.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            Object value = field.get(this);

            // 字段名称
            String fieldName = field.getName();

            // BeanToMap 子类，递归处理
            if (value instanceof BeanToMap) {
                ((BeanToMap) value).toMap(map, clsName + "." + value.getClass().getSimpleName());

                // TODO ArrayList 只处理最后一层，不再递归，后续可以继续递归
            } else if (value instanceof ArrayList) {

                ArrayList list = (ArrayList) value;
                for (int i = 0; i < list.size(); i++) {

                    // 拼接字符串生成数据的 key 逻辑; $.SentryData.tags[1] = [level, info]
                    map.put(clsName + "." + fieldName + "[" + i + "]", list.get(i));
                }

                // POJO 对象或者基本数据类型
            } else {
                map.put(clsName + "." + fieldName, field.get(this));
            }
        }

        return map;
    }

    /**
     * 外部调用接口
     * {@link BeanToMap}子类可以调用，转为 HashMap<String, Object>，要求子类属性在以下条件范围：
     *      1、{@link BeanToMap} 的子类
     *      2、ArrayList
     *      3、POJO 对象
     *      4、基本数据类型
     *      TODO: 改造为对 JSONObject 的装换，而不是 javaBean
     */
    @Deprecated
    public HashMap<String, Object> toMap() throws IllegalAccessException {
        HashMap<String, Object> map = new HashMap<>();
        return toMap(map, "$");
    }

    /**
     * {@link #JSONStringToMap(String jsonString)} 的内部实现
     */
    private static void JSONStringToMap(StringBuilder key, String jsonString, HashMap<String, Object> map) {

        JSONValidator.Type type = JSONValidator.from(jsonString).getType();

        if (type == null) {
            map.put(key.toString(), jsonString);
        } else {
            switch (type) {
                case Object: {

                    try {
                        // JSONObject 递归调用
                        JSONObject jsonObject = JSONObject.parseObject(jsonString);
                        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                            JSONStringToMap(new StringBuilder(key).append(".").append(entry.getKey()), entry.getValue().toString(), map);
                        }
                        break;
                    } catch (Exception e) {
                        log.error("JSONString parse ERROR, type: {}, jsonString:{}", type, jsonString);
                    }

                }
                case Array: {
                    // JSONArray 递归调用
                    JSONArray jsonArray = JSONArray.parseArray(jsonString);
                    for (int i = 0; i < jsonArray.size(); i++) {
                        map.put(key + "[" + i + "]", jsonArray.get(i));
                        // key = jsonArray[i] , jsonString = jsonArray.get(i)
                        JSONStringToMap(new StringBuilder(key).append("[").append(i).append("]"), jsonArray.get(i).toString(), map);
                    }
                    break;
                }
                case Value: {
                    // Value 递归出口
                    map.put(key.toString(), jsonString);
                    break;
                }
                default: {
                    log.error("JSONString parse ERROR ! {}", jsonString);
                    // throw new RuntimeException("JSONString parse ERROR");
                }


            }
        }

    }

    /**
     * 对外接口
     * jsonString 递归转为 Map<String, Object>
     * TODO：JSONValidator.from(jsonString).getType() 存在误判，第一层数组（例如"[1,2,3]"）时，返回的类型是 Object，catch Exception 后再次处理
     */
    @Deprecated
    public static Map<String, Object> JSONStringToMap(String jsonString) {

        // 返回结果
        HashMap<String, Object> resMap = new HashMap<>();

        // '$' 作为 root key
        JSONStringToMap(new StringBuilder("$"), jsonString, resMap);

        return resMap;
    }

    /**
     * 工具包 com.github.wnameless: json-flattener: 0.6.0 实现 jsonString 转为 Map<String, Object>
     */
    public static Map<String, Object> jsonFlatten(String jsonString){

        return JsonFlattener.flattenAsMap(jsonString);

    }

}
